
#include <stdbool.h>
#include <misc.h>
#include "dfruntime.h"
#include <dfruntime_private.h>
#include "page_management.h"
#include "memory_management.h"
#include <boost/unordered_map.hpp>
#include <list>
#include "asyncpages.h"

#include "wish_queue.h"



struct wish_node {
    wish_node( void * _dest,
               size_t _size,
               struct wrvec * _wrvec,
               int _nwrvec,
               struct decvec * _decvec,
               int _ndecvec,
               int _scdec,
               void * _tmp_frame
               ) :
        dest(_dest),
        size(_size),
        wrvec(_wrvec),
        nwrvec(_nwrvec),
        decvec(_decvec),
        ndecvec(_ndecvec),
        scdec(_scdec),
        data(_tmp_frame) {}

    void * dest;
    size_t size;
    struct wrvec * wrvec;
    int nwrvec;
    struct decvec * decvec;
    int ndecvec;
    int scdec;
    void *data;
};


typedef void * PtrType;
typedef boost::unordered_multimap<PtrType,wish_node> WishMap;
typedef std::pair<WishMap::iterator,WishMap::iterator> Range;
static WishMap wish_map;

typedef std::pair<struct frame_struct *,int> decpair;
typedef std::list<decpair> declist_t;




bool wishlist_empty() {
    if (! wish_map.empty() ) {
        DEBUG("Wishlist nonempty : %lu ", (unsigned long) wish_map.size());
    }
    return wish_map.empty();
}


void add_to_wishlist( void * dest,
                      size_t size,
                      struct wrvec * wrvec,
		      int nwrvec,
                      struct decvec * decvec,
                      int ndecvec,
                      int sc_dec,
                      void * tmp_frame ) {
#pragma omp critical (wishlist)
 {
    wish_map.emplace(dest,
                     dest, size,
                     wrvec,nwrvec,
                     decvec, ndecvec,
                     sc_dec,tmp_frame);
}

}


// This function commits the changes described by the wishlist element
void wishlist_commit_page( void * dest ) {
    // Check if destination is acquired :
    bool is_acquired;

    is_acquired = page_is_acquired(dest);


    if ( ! is_acquired ) {
        FATAL( "Commiting delayed write on non acquired page %p", dest);
    }

    int times_to_free = 0;

    // For omp exit restrictions
    // TODO : fix that ...( auxilliary function ?)
    bool return_immediate = false;
    int __z = 1;

    // For external and internal decrements.
    declist_t declist;


#pragma omp critical (wishlist)
{
    // Get all the frames :
    Range results = wish_map.equal_range(dest);
    WishMap::iterator it = results.first;

    if ( results.first == wish_map.end() ) {
        // Then no results :
        return_immediate = true;
        goto end;
    }

    DEBUG("Found wishlist element for : %p", dest);
    // TODO : what about the size ? : /
    // Commit the changes :



    while ( true ) {
        if ( it == results.second ) {
            break;
        }
        // Inspect :
        wish_node &node = it->second;

        for ( int i = 0; i < node.nwrvec; ++i )  {
            memcpy( (void*)((intptr_t)dest + node.wrvec[i].offset),
                    (void*)((intptr_t)node.data + node.wrvec[i].offset),
                    node.wrvec[i].size );

        }

        if ( node.scdec > 0 ) {
            declist.push_front( decpair((struct frame_struct*)node.dest, node.scdec));
        }

        // We must take note of external decreases :
        if ( node.decvec != NULL ) {
            for ( int i = 0; i < node.ndecvec; ++ i) {
                declist.push_back( decpair(node.decvec[i].fp,
                                           node.decvec[i].dec));
            }
        }

        // Then free the memory :
        free( node.data );

        // And increment the iterator :
        CFATAL( (it->first) != dest, "Invalid assuption.");

        times_to_free++;
        it ++;

    }


    // Get rid of them :
    wish_map.erase(results.first,results.second);

    // For omp exit block restrictions :
    end:
    __z=0;
}
    if ( return_immediate ) {
        return;
    }

    // Free the destination :
    for ( int i = 0; i < times_to_free; ++i )  {
        safe_release_page(dest);
    }

    // Tdecreases from declist :
    for ( declist_t::iterator it = declist.begin();
          it != declist.end();
          ++it )  {
        DEBUG( "Tdec from wishlist : %p", it->first);
        for ( int u = 0; u < it->second; ++ u ) {
            dfrt_effective_tdecrease( it->first );
        }
    }




}


